<!DOCTYPE html>
<html>
	
	<head>
		<meta charset="utf-8">

		<!-- !Always force latest IE rendering engine (even in intranet) & Chrome Frame

		Remove this if you use the .htaccess -->
		<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

		<title>11_JavaScript_function</title>
		<meta name="description" content="">
		<meta name="author" content="chenyong">

		<meta name="viewport" content="width=device-width; initial-scale=1.0">
 
		<!-- Replace favicon.ico & apple-touch-icon.png in the root of your domain and delete these references -->
		<link rel="shortcut icon" href="/favicon.ico">
		<link rel="apple-touch-icon" href="/apple-touch-icon.png">
		
		<style type="text/css">
			
			
			body{
				margin:50px;
			}
			
			div{
				font-size:12px;
			}
			
			div.box{
				margin-top:30px;
			}
		
			
		</style>
		
		<script type="text/javascript">
			
			/**
			 * 查看函数的类型 显示为function的对象 
			 */
			function fn(){
				
			}
			
			function fn1(){
				alert('typepf fn：' + typeof fn);
			}
			
			
			/**
			 * 查看函数的赋值方式 
			 */
			
			//定义一个函数f1
			var f1 = function(){
				alert('函数1');
			};
			
			//将f1的函数赋值给f2
			var f2 = f1;
			
			//修改f1对应函数
			f1 = function(){
				alert('修改后的函数1');
			};
			
			//申明一个对象并赋值给a
			var a = new Object();
			a = '对象1';
			
			//将a赋值给b
			var b = a;
			
			//修改a的值
			a = '修改后的对象1';
			
			//验证结果
			function fn2(){
				f2();
				alert(a);
			}
			
			
			/**
			 * 函数重载
			 */
			function sum(a,b){
				return a+b;
			}
			
			function sum(a){
				return a;
			}
			
			function fn3(){
				alert('sum(1)的返回值：'+sum(1));
				alert('sum(1,2)的返回值：'+sum(1,2));
			}
		
		
			/**
			 * 函数的另一种定义方式，充分说明函数是一种对象 
			 */
			var sum2 = new Function("a","b","alert('new Function SUM：'+a+b)");
			function fn4(){
				sum2(2,3);
			}
			
			/**
			 * 函数作为参数 
			 */
			
			function sum(a){
				return a+100;
			}
			
			function hello(a){
				return 'hello ' + a;
			}
			
			function callFunc(func,a){
				return func(a);
			}
			
			function fn5(){
				alert('callFunc(sum,1):' + callFunc(sum,1));
				alert('callFunc(hello,\'bill\'):' + callFunc(hello,'bill'));
			}
			
			/**
			 * 函数作为返回值 
			 */
			function fr(a){
				//加工函数并返回结果
				var func = function(b){
					return a + b;
				};
				return func;
			}
			
			function fn6(){
				//将参数10传给函数fc，fc的功能就变成了对所有存入值加10
				var fc = fr(10);
				alert(fc);
				//验证fc的结果
				alert(fc(11));
			}
			
			var a = new Array(1,3,2,7,'8px','6px','11',113,24);
			function fn7(){
				alert(a.sort(sortByNumber));
			}
			
			/**
			 * 数字升序排序 
			 */
			function sortByNumber(a,b){
				return parseInt(a) - parseInt(b);
			}
			
			var Person = function(name,age,addr){
				this.name = name;
				this.age = age;
				this.addr = addr;
			};
			
			var p1 = new Person('Bill',19,'Los Angeles');
			var p2 = new Person('Adam',22,'New York');
			var p3 = new Person('Curry',26,'San Francisco');
			var p4 = new Person('Dan',21,'Houston');
			var p5 = new Person('Elkeson ',23,'Boston');
			
			var p = [p1,p2,p3,p4,p5];
			
			function show(p){ 
				var str = '';
				for(var i=0;i<p.length;i++){
					str = str + p[i].name + ',' + p[i].age + ',' + p[i].addr + "<br/>";
				}
				document.getElementById('source').innerHTML = str;
			}
			
		
			function fn8(){
				var orderType = document.getElementById('orderType').value;
				var func = orderByPorperty(orderType);
				//console.log(p.sort(func));
				p.sort(func);
				show(p);
			}
			
			
			function orderByPorperty(propertyName){
				var func = function(a,b){
					if(a[propertyName] > b[propertyName]){
						return 1;
					}else if(a[propertyName] == b[propertyName]){
						return 0;
					}else{
						return -1;
					}
				};
				return func;
			}
			
			
			function fnc(a){
				alert('传入参数数量' + arguments.length);
				var str = '参数：';
				for(i=0;i<arguments.length;i++){
					str = str + ' ' + arguments[i];
				}
				alert(str);
				
				alert('函数定义的参数：' + a);
			}
			
			
			function fn9(){
				fnc(1,2,3);
				
			}
			
			
			function fnSum(a){
				if(a == 1){
					return 1;
				}else{
					//实现了函数名的解耦，JS中都使用这种方式进行
					return a+arguments.callee(a-1);
				}
			}
			
			var fc = fnSum;
			
			//如果对fnSum重新赋值fc的执行也会出错 所以
			fnSum = null;
			
			function fn10(){
				alert(fc(5));
			}
			
			window.onload = function(){
				show(p);
			};
			
			
			var color = 'red';
			
			var showColor = function(){
				alert(this.color);
			};
			
			function Item(color){
				this.color = color;
				this.showColor = showColor;
			}
			
			function fn11(){
				var item = new Item('yellow');
				//这里this指向的item
				item.showColor();
				
				//这里this指向window
				showColor();
			}
			
			function len1(a){
				
			}
			function len2(a,b){
				
			}
			function len3(a,b,c){
				
			}
			
			function fn12(){
				alert('len1:' + len1.length);
				alert('len2:' + len2.length);
				alert('len3:' + len3.length);
			}
			
			
			function sum(a,b){
				return a + b;
			}
			
			function applySum(a,b){
				//apply方法将参数封装在数组中传递给函数
				return sum.apply(this,arguments);
				//等价于return sum.apply(this,[a,b]);
			}
			
			function callSum(a,b){
				//call方法需逐个指定参数
				return sum.call(this,a,b);
			}
			
			function fn13(){
				//两个方法的结果都一样
				alert('apply:' + applySum(10,20));
				alert('call:' + callSum(10,20));
			}
			
		</script>
	</head>
	
	<body>
		
			
		<h3>11_JavaScript_函数高级</h3>
		
		<div class="box" >
			<p><b>函数对象</b></p>
			<p>函数是一种特殊的对象</p>
			<p><button onclick="fn1()" style="cursor: pointer">测试1</button></p>
		</div>
		
		<div class="box" >
			<p><b>函数对象引用</b></p>
			<p>普通对象传递的是引用，函数则是对对象的拷贝</p>
			<p><button onclick="fn2()" style="cursor: pointer">测试2</button></p>
			<p>可以看到函数f2并没有随
				着f1的改变而改变，而对象b因为和a都是引用一个对象，当a指向的对象值发生改变的时候，b也随之改变了</p>
		</div>
		
 		
		<div class="box" >
			<p><b>函数重载</b></p>
			<p>在javascript中函数并不存在多态，只是保留最新一次的定义</p>
			<button onclick="fn3()" style="cursor: pointer">测试3</button>
		</div>
		
		<div class="box" >
			<p><b>函数的另外一种定义方式</b></p>
			<p>函数可以通过var sum2 = new Function("a","b","alert('new Function SUM：'+a+b)");这种方式定义。</p>
			<button onclick="fn4()" style="cursor: pointer">测试4</button>
		</div>
		
		<div class="box" >
			<p><b>函数参数</b></p>
			<p>由于函数也是对象，函数可以作为参数进行传递</p>
			<button onclick="fn5()" style="cursor: pointer">测试5</button>
		</div>
		
		<div class="box" >
			<p><b>函数作为返回值</b></p>
			<p>函数可以作为函数的返回值，通过这样，可以修改函数的内部参数</p>
			<button onclick="fn6()" style="cursor: pointer">测试6</button>
		</div>
		
		
		<div class="box" >
			<p><b>排序函数</b></p>
			<p>sort默认不带参数的时候，是按照字符排序的。如果需要改变的值</p>
			<p>比较函数应该具有两个参数 a 和 b，其返回值如下：</p>
			<p>若 a 小于 b，在排序后的数组中 a 应该出现在 b 之前，则返回一个小于 0 的值。 </p>
			<p>若 a 等于 b，则返回 0。 </p>
			<p>若 a 大于 b，则返回一个大于 0 的值。 </p>

			<button onclick="fn7()" style="cursor: pointer">测试7</button>
			
			
			<p><b>对象排序：</b>通过
			<select name="orderType" id="orderType" onchange="fn8()">
				<option value="">-请选择-</option>
				<option value="name">姓名</option>
				<option value="age">年龄</option>
				<option value="addr">地址</option>
			</select>
			排序
			</p>
			<p id="source"> </p>
		</div>
		
		
		<div class="box" >
			<p><b>函数内部属性arguments</b></p>
			<p>函数的参数函数对象的arguments属性中，可以被访问到</p>
			<p>argument是一个保存函数参数的一个数组，传入的所有参数都会被保存，与函数定义的参数数量无关</p>
			<button onclick="fn9()" style="cursor: pointer">测试9</button>
		</div>
			
		<div class="box" >
			<p><b>函数内部反向调用</b></p>
			<p>函数内部递归调用函数体时需通过arguments.callee()反向调用，否则函数名重定义可能会遇到失效的问题</p>
			<button onclick="fn10()">测试10</button>
		</div>
		
		<div class="box" >
			<p><b>this关键字</b></p>
			<p>同一个函数，不同上下文调用的this指向也不一样</p>
			<button onclick="fn11()">测试11</button>
		</div>
		
		<div class="box" >
			<p><b>length属性</b></p>
			<p>length表示函数所期望的参数个数</p>
			<button onclick="fn12()">测试12</button>
		</div>
		
		<div class="box" >
			<p><b>call和apply方法</b></p>
			<p>js提供call和apply方法对以函数名作为参数进行调用。</p>
			<p>apply方法函数参数封装在数组中</p>
			<p>call方法函数参数需按顺序逐个指定</p>
			<button onclick="fn13()">测试13</button>
		</div>
	</body>
	
</html>
